
package JTChat;


import java.util.Calendar;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;

import Jt.JtContext;
import Jt.JtFactory;
import Jt.JtMessage;
import Jt.JtObject;




public class Chat extends JtObject {



    public static final String JtCLASS_NAME = Chat.class.getName(); 
    //public static final int MAX_DESCRIPTION_SIZE = 15; 
    public static final String JtSEND = "JtSEND"; 
    public static final String JtRECEIVE = "JtRECEIVE"; 
    public static final String KEYBOARD_EVENT = "KEYBOARD_EVENT"; 
    public static final String CONNECT = "CONNECT"; 
    public static final String CONNECT_MESSAGE = "- Connected."; 
    private static Chat chat=null;

    private static final long serialVersionUID = 1L;

    //private long chatId;
    public String description;
    public long sequence = 0L;

    public LinkedList messageQueue = new LinkedList ();
    public Hashtable users = new Hashtable ();
    public String lastMessages;
    public String statusMessage;
    public long maxAge = 30000L;
    
    
    
    transient JtFactory factory = new JtFactory ();
    transient JtContext context;
   
   /*  Singleton  */
    public static Chat getInstance()
    {
        if (chat==null)
            chat=new Chat();
        return chat;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
 

    public String getLastMessages() {
        return lastMessages;
    }

    public void setLastMessages(String lastMessages) {
        this.lastMessages = lastMessages;
    }

    public String getStatusMessage() {
        return statusMessage;
    }

    public void setStatusMessage(String statusMessage) {
        this.statusMessage = statusMessage;
    }

    public long getMaxAge() {
        return maxAge;
    }

    public void setMaxAge(long maxAge) {
        this.maxAge = maxAge;
    }

    /*
    private Exception propagateException (JtObject obj)
    {
        Exception ex;

        if (obj == null)
            return null;

        ex = (Exception) obj.getObjException();

        if (ex != null)
            this.setObjException(ex);

        return (ex);
    }
    */
    
    
    public synchronized void openConnection (String username) { 
        ChatMessage cMessage = new ChatMessage ();
        
        if (username == null || username.equals("")) {
            handleError ("Invalid username");
            return;
        }
        
        if (users.get(username) != null)
            return;
        
        retrieveConnection (username);
        cMessage.setMessage(Chat.CONNECT_MESSAGE);
        cMessage.setTimeStamp(new Date());
        cMessage.setUsername(username);
        cMessage.setSequence (++sequence); 
        
        messageQueue.add(cMessage);
    }
    
    public synchronized void sendChatMessage (String username, String message) { 
       ChatMessage cMessage = new ChatMessage ();
        
        if (username == null || username.equals("")) {
            handleError ("Invalid username");
            return;
        }
        
        if (message == null || message.equals("")) {
            handleError ("Invalid Message");
            return;
        }
        
        retrieveConnection (username);
        cMessage.setMessage(message);
        cMessage.setTimeStamp(new Date());
        cMessage.setUsername(username);
        cMessage.setSequence (++sequence); 
        
        messageQueue.add(cMessage);
    }
    
    

    public synchronized void handleKeyboardEvent (String username) { 
        ChatMessage cMessage = new ChatMessage ();
        
        if (username == null || username.equals("")) {
            handleError ("Invalid username");
            return;
        }
        
        
        retrieveConnection (username);
        cMessage.setTimeStamp(new Date());
        cMessage.setUsername(username);
        cMessage.setSequence (++sequence);
        cMessage.setKeyboardEvent(true);
        
        messageQueue.add(cMessage);
    }
    
    
/*
    private ChatConnection connect (String username) {
        ChatConnection connection;
        
        if (username == null || username.equals (""))
            return (null);
        
        connection = new ChatConnection ();
        
        connection.setSequence(0L);
        
        users.put(username, connection);
        
        return (connection);
        
    }
*/
    
    public  ChatConnection retrieveConnection (String username) {
        ChatConnection connection;
        
        if (username == null)
            return (null);
        
        connection = (ChatConnection) users.get(username);
        if (connection == null) {
            connection = new ChatConnection ();
            connection.setSequence(0L);
            users.put(username, connection);
        }
        
        return (connection);
        
    }
    
    public long messageAge (ChatMessage message) {
        Date timestamp;
        Date now = new Date ();
        
        if (message == null)
            return (0L);
        
        timestamp = message.getTimeStamp();
        
        if (timestamp == null)
            return (0L);
        
        return (now.getTime() - timestamp.getTime());
        
    }
    
    public synchronized void houseKeeping () {
        int i;
        ChatMessage message;
        
        
        if (messageQueue.isEmpty())
            return;
        
        if (maxAge == 0L)
            return;
        
        for (i = 0; i < messageQueue.size(); i++) {
            message = (ChatMessage) messageQueue.get(i);
            
            if (messageAge (message) > maxAge)
               messageQueue.remove(i);
            else
                break;
        }
        
        
    }
    
    public String retrieveSTime (Date date) {
        Calendar calendar = Calendar.getInstance();
        if (date == null)
            return ("");
        
        calendar.setTime(date);
        return ("" + calendar.get (Calendar.HOUR) + ":" + calendar.get (Calendar.MINUTE)
                + ":" + calendar.get (Calendar.SECOND));
        
        
    }
    
    public synchronized Chat receiveMessages (String username) {
        ChatConnection connection;
        StringBuffer sBuffer = new StringBuffer ();
        Iterator iterator;
        ChatMessage cMessage;
        long newSequence = 0L;
        boolean change = false;
        Chat rChat = new Chat ();
        String statusMessage;
        String statusUsername = null;
        
        rChat.setStatusMessage("");
        
        if (username == null) {
            handleError ("Invalid username: null");
            return (null);
        }
        /*
        connection = (ChatConnection) users.get(username);
        if (connection == null)
            connection = connect (username);
        */
        
        connection = retrieveConnection (username);
        
        if (connection == null)
            return (null);
        
        if (messageQueue.isEmpty()) {
            rChat.setLastMessages("");
            rChat.setStatusMessage("");
            handleTrace ("Chat.receiveMessages: empty string");
            return (rChat);
        }
        
        iterator = messageQueue.iterator();
        
        while (iterator.hasNext()) {
            cMessage = (ChatMessage) iterator.next();
            

            
            if (cMessage.getSequence() <= connection.getSequence())
                continue;
            
            
            if (cMessage.isKeyboardEvent()) {
                
                if (username.equals(cMessage.getUsername())) {
                    continue;
                }
                statusMessage = cMessage.getUsername() + " is typing a message.";
                rChat.setStatusMessage(statusMessage);
                statusUsername = cMessage.getUsername();
                    
                newSequence = cMessage.getSequence ();
                connection.setSequence(newSequence);
                
                users.put (username, connection);
                continue;
            }
            
            //if (username.equals(cMessage.getUsername())) {
            //    continue;                
            //}
            
            if (statusUsername != null && statusUsername.equals(cMessage.getUsername()))
                rChat.setStatusMessage("");
            
            change = true;
            sBuffer.append(cMessage.getUsername() + " " +
                    //cMessage.getTimeStamp() + " " +
                    retrieveSTime (cMessage.getTimeStamp()) + " " +
                    cMessage.getMessage() + "\n");
            
            newSequence = cMessage.getSequence ();
            
        }
        
        if (!change) {
            handleTrace ("Chat.receiveMessages: empty string");
            rChat.setLastMessages("");
            return (rChat);
        }
        // Update connection
        
        connection.setSequence(newSequence);
        
        users.put (username, connection);
        
        handleTrace ("Chat.receiveMessages:" + sBuffer.toString());
        
        rChat.setLastMessages(sBuffer.toString());
        return (rChat);
        
    }
    /**
     * Process object messages.
     * <ul>
     * </ul>
     * @param message Jt Message
     */

    public Object processMessage (Object message) {

        String msgid = null;
        JtMessage e = (JtMessage) message;
        Object content;
        //ActionForm form = null;



        if (e == null)
            return null;

        msgid = (String) e.getMsgId ();

        if (msgid == null)
            return null;


        content = e.getMsgContent();
        context = (JtContext) e.getMsgContext();

        if (context == null || context.getUserName() == null) {
            handleError ("Invalid context.");
            return (null);
        }
        
        if (e.getMsgId().equals (Chat.JtSEND)) {
 
            
            sendChatMessage (context.getUserName(), (String) content);
            return (null);

        }
        
        if (e.getMsgId().equals (Chat.JtRECEIVE)) {
            
           houseKeeping (); 
           return (receiveMessages (context.getUserName()));
        }

        if (e.getMsgId().equals (Chat.KEYBOARD_EVENT)) {
            handleKeyboardEvent (context.getUserName());
            return (null);
        }
        
        if (e.getMsgId().equals (Chat.CONNECT)) {
            
            openConnection (context.getUserName());
            return (null);
         }
       
        return (super.processMessage (message));



    }
 

}